<?php

namespace App\Kuafu\WaiMai\Command;

use App\Kuafu\WaiMai\Model\WaiMaiCallbackLogModel;
use App\Library\FeiShu\SendFs;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class WaiMaiBak extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'waimai:bak {forceOptimize?} {bakDate?}';

    /**ø
     * The console command description.
     *
     * @var string
     */
    protected $description = '删除并备份回调表数据';

    /**
     * Create a new command instance.
     *
     * @return void//
     */
    public function __construct()
    {
        parent::__construct();

        $this->checkOptimize = $this->checkOptimize();
    }

    private string $errs = '';
    private array  $logs = [];
    private int    $i;
    private bool   $checkOptimize;
    private bool   $forceOptimize = false;

    /**
     * Execute the console command
     *
     * @return int
     */
    public function handle()
    {
        @ini_set('memory_limit', '-1');

        // 强制执行optimize
        $forceOptimize = $this->argument("forceOptimize");
        if ($forceOptimize){
            $this->forceOptimize = (bool) $forceOptimize;
        }

        // 备份数据的日期
        $bakDate = $this->argument('bakDate');
        if (!$bakDate) {
            $bakDate = date('Ymd', time() - 86400 * 90);

        }

        // db配置
        $connection = 'mysqlWaiMai';

        $dbConfig = config("database.connections.{$connection}");

        $host     = $dbConfig['host'];
        $port     = $dbConfig['port'];
        $username = $dbConfig['username'];
        $password = $dbConfig['password'];
        $database = $dbConfig['database'];
        $prefix   = $dbConfig['prefix'];

        if (config('app.env') == 'dev') {
            $filePath = "~/Downloads/rds/{$bakDate}";
        } else {
            $filePath = "/mnt/backup/rds/{$bakDate}";
        }

        exec("mkdir -p {$filePath}");

        for ($this->i = 0; $this->i < 100; $this->i++) {

            // 查询最大id
            $table = $prefix . WaiMaiCallbackLogModel::suffix($this->i)->getTable();
            $maxId = (int)WaiMaiCallbackLogModel::suffix($this->i)
                                                ->where('createdAt', '<', strtotime($bakDate) + 86400)
                                                ->orderByDesc('id')
                                                ->value('id');

            if (!$maxId) {

                $this->log($table . 'empty row...');
                continue;
            }

            $this->log("WaiMaiBak {$table} maxId:{$maxId} mysqldump start...", self::STEP_START);

            // mysqldump
            $fileName   = "{$table}.sql";
            $resultCode = null;
            $output     = "";
            exec("mysqldump -h{$host} -P{$port} -u{$username} -p{$password} --add-locks=0 --no-create-info --single-transaction {$database} {$table} --where=\"id<{$maxId}\"  > {$filePath}/{$fileName}",
                $output, $resultCode);

            // mysqldump - error
            if ($resultCode !== 0) {
                $this->log("WaiMaiBak {$table} maxId:{$maxId} mysqldump error...");

                $this->errs .= "{$table} mysqldump err...:{$output}";

                continue;
            }

            $this->log("WaiMaiBak {$table} maxId:{$maxId} mysqldump success...", self::STEP_MYSQLDUMP);

            // delete
            $deletePage = 10000;
            do {
                $delNum = WaiMaiCallbackLogModel::suffix($this->i)
                                                ->where('id', '<=', $maxId)
                                                ->orderBy('id')
                                                ->limit($deletePage)
                                                ->delete();

                $this->log("{$this->i} delete {$delNum}");

            } while ($delNum == $deletePage);

            $this->log("{$this->i} delete success..........", self::STEP_DELETE);

            // optimize
            if ($this->checkOptimize) {
                DB::connection($connection)->statement("ALTER TABLE {$table} ENGINE=InnoDB");
                DB::connection($connection)->statement("ANALYZE TABLE {$table};");

                $this->log("{$this->i} optimize success", self::STEP_OPTIMIZE);
            }
        }

        if ($this->errs) {
            $this->log($this->errs);
        } else {
            $this->log('success');
        }

        // 输出结果
        $this->printResult();
    }

    const STEP_START     = 1; // start
    const STEP_MYSQLDUMP = 2; // mysqldump
    const STEP_DELETE    = 3; // delete
    const STEP_OPTIMIZE  = 4; // optimize

    const STEP_TEXT = [
        self::STEP_START     => 'start',
        self::STEP_MYSQLDUMP => 'mysqldump',
        self::STEP_DELETE    => 'delete',
        self::STEP_OPTIMIZE  => 'optimize',
    ];

    private function log(string $str, int $type = 0): void
    {
        $this->info($str);
        Log::info($str);

        if ($type > 0) {
            $this->logs[$this->i][] = ['type' => $type, 't' => time()];
        }
    }

    /**
     * 是否处理optimize
     * @return bool
     */
    private function checkOptimize(): bool
    {
        if ($this->forceOptimize) {
            return true;
        }

        // 每个月的第一个周三处理
        $target = strtotime("first Wednesday this month", strtotime(date('Ym') . '01'));

        $today = strtotime(date('Ymd'));
        if ($today == $target) {
            return true;
        }

        return false;
    }

    /**
     * 输出处理的时间统计结果
     * @return void
     */
    private function printResult(): void
    {
        $msg = "";
        foreach ($this->logs as $i => $log) {
            $msg .= "{$i}:";
            foreach ($log as $k => $val) {
                if ($k == 0) {
                    continue;
                }

                $msg .= self::STEP_TEXT[$val['type']] . " : " . ($log[$k]['t'] - $log[$k - 1]['t']) . "s \n";
            }


            $msg .= "\n";
        }

        if ($msg) {
            $this->info($msg);

            SendFs::newRobot(SendFs::ROBOT_INTERNAL)
                  ->setTitleContent("每日备份waimai_callback_log", $msg)
                  ->send();
        }
    }
}
